package com.wlyuan.index.domain.index.service;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.wlyuan.elasticsearch.builder.QueryBuilderFactory;
import com.wlyuan.elasticsearch.builder.Specification;
import com.wlyuan.tools.PageSort;
import com.wlyuan.tools.Sort;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yuanjie
 */
@Slf4j
public abstract class AbstractSearchService {
    protected final QueryBuilderFactory factory = new QueryBuilderFactory();
    protected final List<SortBuilder<?>> defaultSorts = new ArrayList<>();
    protected final Set<String> ignoreNames = new HashSet<String>() {{
        add("page");
        add("size");
        add("sort");
        add("userId");
    }};

    protected void addDefaultSort(String fieldName, SortOrder order) {
        FieldSortBuilder sortBuilder = new FieldSortBuilder(fieldName);
        sortBuilder.order(order);
        defaultSorts.add(sortBuilder);
    }

    protected List<SortBuilder<?>> createSorts(List<Sort> sorts) {
        if (ObjectUtil.isEmpty(sorts)) {
            return defaultSorts;
        }
        List<SortBuilder<?>> sortBuilders = new ArrayList<>(sorts.size());
        sorts.forEach(sort -> {
            FieldSortBuilder builder = new FieldSortBuilder(sort.getName());
            builder.order(sort.isAsc() ? SortOrder.ASC : SortOrder.DESC);
            sortBuilders.add(builder);
        });
        sortBuilders.addAll(defaultSorts);
        return sortBuilders;
    }

    protected <T extends PageSort> SearchSourceBuilder createBuilder(T filter, Map<String, Specification> rules) {
        var sourceBuilder = factory.createSourceBuilder(filter, rules, ignoreNames)
                .from((int) filter.getStart())
                .size((int) filter.getSize());

        sourceBuilder.sort(createSorts(filter.getSort()));
        return sourceBuilder;
    }

    protected <T> SearchSourceBuilder createBuilder(List<T> filters, Map<String, Specification> rules, PageSort page) {
        var sourceBuilder = factory.createSourceBuilder(filters, rules, ignoreNames)
                .from((int) page.getStart())
                .size((int) page.getSize());

        sourceBuilder.sort(createSorts(page.getSort()));
        return sourceBuilder;
    }

    protected <T extends PageSort> SearchRequest createRequest(T filter, Map<String, Specification> rules) {
        var sourceBuilder = createBuilder(filter, rules);
        SearchRequest request = new SearchRequest();
        request.indices(getIndexName());
        request.source(sourceBuilder);
        if (logger.isDebugEnabled()) {
            logger.debug("查询索引: {} {}", getIndexName(), sourceBuilder);
        }
        return request;
    }

    protected <T> SearchRequest createRequest(List<T> filters, Map<String, Specification> rules, PageSort page) {
        var sourceBuilder = createBuilder(filters, rules, page);
        SearchRequest request = new SearchRequest();
        request.indices(getIndexName());
        request.source(sourceBuilder);
        if (logger.isDebugEnabled()) {
            logger.debug("查询索引: {} {}", getIndexName(), sourceBuilder);
        }
        return request;
    }

    protected RequestOptions createRequestOptions(Long tenantId) {
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
        builder.addParameter("preference", "u:" + tenantId);
        return builder.build();
    }


    protected <T extends PageSort> SearchRequest createScrollRequest(T filter, Map<String, Specification> rules) {
        return createRequest(filter, rules).scroll("2m");
    }

    protected SearchScrollRequest createScrollRequest(String scrollId) {
        return new SearchScrollRequest(scrollId).scroll("2m");
    }


    protected <T> List<T> createIndices(SearchHits hits, Class<T> indexClass) {
        return Arrays.stream(hits.getHits())
                .map(hit -> {
                    var sourceString = hit.getSourceAsString();
                    // FIXME: 时间多8小时间问题， 时间后面多了个Z，表示的是零时区
                    return JSON.parseObject(sourceString, indexClass, Feature.AllowISO8601DateFormat);
                })
                .collect(Collectors.toList());
    }

    abstract protected String getIndexName();
}
